feat(mysql/user): support non-default authentication plugins for IAM auth#363
feat(mysql/user): support non-default authentication plugins for IAM auth#363ronlevy1211 wants to merge 8 commits into
Conversation
…auth
Add an optional AuthenticationPlugin field on the MySQL User resource. When
set, the user is created with CREATE USER ... IDENTIFIED WITH <plugin>
[AS '<authString>'] instead of IDENTIFIED BY <password>. No password is
generated, the connection secret omits the password key, and ALTER USER
... IDENTIFIED BY is skipped on Update so the provider does not downgrade
plugin-auth users to native password auth.
Primary motivation is AWS RDS IAM authentication, where the DB user has
no static password and clients fetch a short-lived auth token from AWS
at connect time:
spec:
forProvider:
authenticationPlugin:
name: AWSAuthenticationPlugin
authString: RDS
The same field also works for other auth plugins (auth_socket,
caching_sha2_password, etc.).
Backward compatible: when AuthenticationPlugin is unset the existing
password-based flow is preserved.
Applied to both the cluster and namespaced API variants. Plugin name is
quoted as a SQL identifier; auth string is quoted as a SQL value.
Signed-off-by: Ron Levy <rlevy@fireblocks.com>
E2E test resultsValidated end-to-end against an AWS Aurora MySQL (Aurora 3 / MySQL 8.0 compatible) on a non-prod cluster. Built the provider from this branch as a custom OCI image and deployed it on Crossplane pointing at the cluster. SetupapiVersion: mysql.sql.crossplane.io/v1alpha1
kind: User
metadata:
name: iam-auth-test
annotations:
crossplane.io/external-name: iam-auth-test
spec:
forProvider:
authenticationPlugin:
name: AWSAuthenticationPlugin
authString: RDS
providerConfigRef:
name: mysql-shell-services
writeConnectionSecretToRef:
name: iam-auth-test-conn
namespace: defaultPlus a Results
Direct DB query proving the plugin was applied: |
|
Hi @fernandezcuesta @chlunde @JevgeniF — friendly ping for a first pass when you have a moment. The PR adds |
…ually exclusive Add a CRD-level XValidation rule rejecting any User where both passwordSecretRef and authenticationPlugin are set, addressing review feedback. The runtime branches in Observe/Create/UpdatePassword stay because they encode the semantic that plugin-auth users have no password (they are not defensive guards against both fields being set). Tighten doc comments on both fields to reflect that the two cannot coexist. Signed-off-by: Ron Levy <rlevy@fireblocks.com>
3763ae5 to
af1d37f
Compare
|
Hey @fernandezcuesta — no rush, just a gentle nudge in case this slipped past. Pushed af1d37f last Monday with the Also pinging @chlunde @JevgeniF for any second opinion if Fernando is heads-down elsewhere. |
chlunde
left a comment
There was a problem hiding this comment.
Could you add drift detection, or did you already discuss this?
Address @chlunde's review on crossplane-contrib#363: Observe didn't read the user's actual `plugin` from mysql.user, and Update silently ignored authenticationPlugin changes. Crossplane managed resources should reconcile every field the upstream system supports. Changes: Observe — extend the SELECT to also fetch `plugin` and `authentication_string`. Hydrate observed.AuthenticationPlugin only when the observed plugin is a non-default-password plugin (caching_sha2_password / mysql_native_password / sha256_password represent "use a password" rather than an opt-in plugin choice and are left as nil in the observed state). upToDate — compare AuthenticationPlugin (name + authString). nil vs non-nil is a drift; same nil-ness with matching name+authString is up-to-date. Update — when authenticationPlugin drift is detected and the desired spec sets a plugin, emit ALTER USER <user>@<host> IDENTIFIED WITH <plugin> [AS '<authString>'] via a new executeAlterUserWithPluginQuery helper that mirrors the Create-time query's quoting rules (identifier-quote the plugin name, value-quote the authString). When the desired spec drops the plugin (plugin → password transition), the existing UpdatePassword path is reused — ALTER USER ... IDENTIFIED BY '<pw>' both sets the password and restores the default password plugin in one statement. Status — UserObservation gains an AuthenticationPlugin field, set by Observe and consumed by Update for drift detection across reconciliation cycles. Tests cover three new transitions: - password → plugin (observed nil, desired set) - plugin → different authString (same name, different AS clause) - existing AuthenticationPluginSkipsAlterPassword updated to set observed == desired and now asserts no IDENTIFIED WITH OR BY runs Signed-off-by: ronlevy <rlevy@fireblocks.com>
CI on the post-master-merge HEAD failed on two checks: - lint (gocyclo) — the Update function in both cluster and namespaced reconcilers had cyclomatic complexity 11 (limit 10). Extracted the resource-options drift branch into a small updateResourceOptions helper. Behavior unchanged: same ALTER USER WITH <opts> emission, same status writeback, same error wrapping. - check-diff — package/crds/*.yaml were stale: the AuthenticationPlugin field added to UserObservation in the previous commit was not yet reflected in the generated CRDs. Ran `make generate` so the rendered schema matches the typed Go source. Tests pass on both packages. Signed-off-by: ronlevy <rlevy@fireblocks.com>
3ee8678 to
a3aedb1
Compare
|
Hi @chlunde, gentle bump on this. Drift detection from your last review is added (commit |
|
Hi @chlunde, picking this up after some time away. To recap what changed since your last review: drift detection was added in Could you let me know if this matches what you had in mind, or if there's a different shape you'd prefer? Happy to iterate. |
|
Claude found another issue, can you check it? Setting spec.forProvider.authenticationPlugin.name to a password plugin (mysql_native_password, caching_sha2_password, sha256_password) makes the MySQL User reconcile forever, because Observe never hydrates status.atProvider.authenticationPlugin for those plugins, so upToDate can never return true. |
Description of your changes
Adds an optional
AuthenticationPluginfield on the MySQLUserresource. When set, the user is created withCREATE USER ... IDENTIFIED WITH <plugin> [AS '<authString>']instead ofIDENTIFIED BY <password>. No password is generated, the connection secret omits the password key, andALTER USER ... IDENTIFIED BYis skipped on Update so the provider does not downgrade plugin-auth users to native password auth.Primary motivation is AWS RDS IAM authentication, where the DB user has no static password and clients fetch a short-lived auth token from AWS at connect time:
The same field also works for other auth plugins (
auth_socket,caching_sha2_password, etc.).Backward compatibility
When
AuthenticationPluginis unset, the existing password-based flow is preserved. Existing users / manifests are unaffected.Notable changes
AuthenticationPlugin{Name, AuthString}struct onUserParameters(cluster + namespaced variants)Create: branches toIDENTIFIED WITHwhen the plugin is set; password generation skippedObserve: skips password drift check when the plugin is set (no password to drift)UpdatePassword: no-op when the plugin is set (avoids downgrading the user back to password auth)examples/cluster/mysql/user_iam_auth.yamlandexamples/namespaced/mysql/user_iam_auth.yamlFixes #106
I have:
make reviewableto ensure this PR is ready for review.How has this code been tested